﻿#include "winProgress.h"

namespace LAULL_PROGRESS_WIN {

#ifdef UNICODE
#define tcharlen wcslen
#define tstring std::wstring
#else
#define tcharlen strlen
#define tstring std::string
#endif // UNICODE

    static HFONT SHOW_FONT;

    static LaullProgressItems* tem_item_p = nullptr;

    static std::map<HWND, LaullProgressItems*> item_map{};

    void _CloseProgressWindow(int exit_message, struct LaullProgressItems* item_p);

    tstring get_name_and_rand_str() {
        const int rand_len = 8;
        TCHAR chars[] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'};
        size_t chars_len = sizeof(chars) / sizeof(TCHAR);
        tstring str = TEXT("LAULL_WIN_PROGRESS_");
        for (int i = 0; i < rand_len; i++) {
            str += chars[ rand() % chars_len ];
        }
        //wprintf(L"%s\n", str.c_str());
        return str;
    }

    LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
        switch (msg) {
        case WM_CREATE: {
            LaullProgressItems* item_p = tem_item_p;
            ULONGLONG can = 0;
            if (item_p->ALLOW_CANCEL)
                can = WS_VISIBLE;
            else
                can = WS_DISABLED;
            item_p->button_cancel.hwnd = CreateWindow(TEXT("BUTTON"), TEXT("取消"),
                can | WS_CHILD | BS_PUSHBUTTON,
                80, 115, 200, 30,
                hwnd, (HMENU)item_p->button_cancel.hmenu,
                GetModuleHandle(NULL), item_p);
            PostMessage(item_p->button_cancel.hwnd, WM_SETFONT, (WPARAM)SHOW_FONT, TRUE);

            item_p->progress_bar.hwnd = CreateWindow(PROGRESS_CLASS, TEXT(""),
                WS_CHILD | WS_VISIBLE | PBS_SMOOTH,
                30, 65, 300, 30,
                hwnd, (HMENU)item_p->progress_bar.hmenu,
                GetModuleHandle(NULL), NULL);
            PostMessage(item_p->progress_bar.hwnd, PBM_SETRANGE, 0, MAKELPARAM(0, item_p->_total));
            PostMessage(item_p->progress_bar.hwnd, PBM_SETPOS, item_p->_init_num, 0);
            break;
        }
        case WM_PAINT:
        {
            LaullProgressItems* item_p = item_map[hwnd];
            PAINTSTRUCT ps;
            HDC texthdc = BeginPaint(hwnd, &ps);
            RECT textrect;
            GetClientRect(hwnd, &textrect);
            HFONT hOldFont = (HFONT)SelectObject(texthdc, SHOW_FONT);
            // 设置文本颜色
            SetTextColor(texthdc, RGB(0, 0, 0));
            SetBkMode(texthdc, TRANSPARENT);
            FillRect(texthdc, &textrect, (HBRUSH)(COLOR_WINDOW + 1));
            //DrawTextW(hdc, text, -1, &rect, DT_SINGLELINE | DT_VCENTER);
            TextOut(texthdc, 40, 35, item_p->use_text, tcharlen(item_p->use_text));
            SelectObject(texthdc, hOldFont);
            EndPaint(hwnd, &ps);
            break;
        }


        case WM_COMMAND:
        {
            LaullProgressItems* _item_p = item_map[hwnd];
            if (LOWORD(wParam) == _item_p->button_cancel.hmenu) // 检查是哪个按钮
            {
                switch (HIWORD(wParam))
                {
                case BN_CLICKED: // 取消按钮被点击
                    if (_item_p->ALLOW_CANCEL) {
                        _CloseProgressWindow(-1, _item_p);
                    }
                    else
                        MessageBox(NULL, TEXT("暂时无法取消"), TEXT("取消"), MB_OK | MB_ICONINFORMATION);
                    break;
                }
            }
            break;
        }
        case WM_DESTROY:
        case WM_CLOSE:
            _CloseProgressWindow(-2, item_map[hwnd]); // 否则无法正常退出
            goto qqq;
        default:
        qqq:
            return DefWindowProc(hwnd, msg, wParam, lParam);
        }
        return 0;
    }

    bool InitProcessarWindow(HWND father, LPCTSTR classname, LPCTSTR title, ULONGLONG total, ULONGLONG init_num, BOOL allow_cancel, struct LaullProgressItems* item_p, int x, int y) {
        // 设置字体
        SHOW_FONT = CreateFont(20, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
            OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
            DEFAULT_PITCH | FF_SWISS, TEXT("Arial"));
        item_p->ALLOW_CANCEL = allow_cancel;
        item_p->_total = total;
        item_p->_init_num = init_num;
        WNDCLASSEX wc = { sizeof(WNDCLASSEX) };
        wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);

        wc.hInstance = GetModuleHandle(NULL);

        wc.lpszClassName = classname;
        wc.lpfnWndProc = WindowProcedure;

        if (!RegisterClassEx(&wc)) {
            MessageBox(NULL, TEXT("生成窗口错误_1"), TEXT("错误"), MB_ICONEXCLAMATION | MB_OK);
            return false;
        }
        tem_item_p = item_p;
        HWND hwnd = CreateWindowExW(
            WS_EX_CLIENTEDGE, // 窗口样式
            classname,   // 窗口类名
            title,   // 窗口标题
            WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX, // 窗口样式
            x,    // 初始x位置
            y,    // 初始y位置
            hight,    // 宽度
            width,    // 高度
            father,           // 父窗口
            NULL,             // 无菜单
            //hInst,          // 程序实例句柄
            NULL,
            NULL);            // 额外的窗口创建数据

        if (hwnd == NULL) {
            MessageBox(NULL, TEXT("生成窗口错误_2"), TEXT("错误"), MB_ICONEXCLAMATION | MB_OK);
            return false;
        }
        item_p->hwnd = hwnd;
        item_map[hwnd] = item_p;
        return true;
    }

    // 取消返回-1, 关闭返回-2, 手动关闭返回1
    int ShowProcessWindow(struct LaullProgressItems* item_p) {
        ShowWindow(item_p->hwnd, SW_NORMAL);
        //UpdateWindow(item_p->hwnd);
        PostMessage(item_p->hwnd, WM_PAINT, NULL, (LPARAM)item_p);
        // MessageBox(NULL, TEXT("生成窗口"), TEXT(""), MB_ICONEXCLAMATION | MB_OK);
        MSG msg = { 0 };
        while (GetMessage(&msg, NULL, 0, 0) > 0) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return item_p->win_return;
    }


    void _CloseProgressWindow(int exit_message, struct LaullProgressItems* item_p) {
        item_p->win_return = exit_message;
        PostMessage(item_p->hwnd, WM_QUIT, 0, 0);
    }
}


void WinProgress::init(HWND father, LPCTSTR classname, LPCTSTR title, ULONGLONG total, ULONGLONG init_num, BOOL allow_cancel) {
    using namespace LAULL_PROGRESS_WIN;
    if (this->x == NULL)
        this->x = (GetSystemMetrics(SM_CXSCREEN) - hight) / 2;
    if (this->y == NULL)
        this->y = (GetSystemMetrics(SM_CYSCREEN) - width) / 2;

    t = std::thread([&] {
        //this->item_p = new LaullProgressItems;
        bool done = InitProcessarWindow(father, classname, title, total, init_num, allow_cancel, item_p, x, y);
        if (!done) {
            inited = true;
            finish = true;
            return;
        } 
        inited = true;
        RETURN = ShowProcessWindow(item_p);
        finish = true;
        });
    t.detach();
    while (!inited)
        Sleep(10);
}

WinProgress::WinProgress(HWND father, LPCTSTR classname, LPCTSTR title, ULONGLONG total, ULONGLONG init_num, BOOL allow_cancel, int position_x, int position_y)
{
    this->x = position_x;
    this->y = position_y;
    init(father, classname, title, total, init_num, allow_cancel);
}

WinProgress::WinProgress(HWND father, LPCTSTR title, ULONGLONG total, ULONGLONG init_num, BOOL allow_cancel)
{
    using namespace LAULL_PROGRESS_WIN;
    srand(time(0));
    init(father, get_name_and_rand_str().c_str(), title, total, init_num, allow_cancel);
    
}

WinProgress::WinProgress(HWND father, LPCTSTR title, ULONGLONG total, ULONGLONG init_num, BOOL allow_cancel, int position_x, int position_y)
{
    using namespace LAULL_PROGRESS_WIN;
    this->x = position_x;
    this->y = position_y;
    srand(time(0));
    init(father, get_name_and_rand_str().c_str(), title, total, init_num, allow_cancel);
}

WinProgress::WinProgress(HWND father, LPCTSTR classname, LPCTSTR title, ULONGLONG total, ULONGLONG init_num, BOOL allow_cancel)
{
    init(father, classname, title, total, init_num, allow_cancel);
}

void WinProgress::WinProgress::Close() {
    using namespace LAULL_PROGRESS_WIN;
    _CloseProgressWindow(1, item_p);
}

bool WinProgress::IsFinish(){
    return this->finish;
}

void WinProgress::SetText(const TCHAR text[]) {
    using namespace LAULL_PROGRESS_WIN;
#ifdef UNICODE
    swprintf(item_p->use_text, L"%ls", text);
#else
    sprintf(use_text, "%s", text);
#endif
    InvalidateRect(item_p->hwnd, NULL, FALSE);
    //PostMessage(item_p->hwnd, WM_PAINT, NULL, (LPARAM)item_p);
}

ULONGLONG WinProgress::GetProgress() {
    using namespace LAULL_PROGRESS_WIN;
    return SendMessage(item_p->progress_bar.hwnd, PBM_GETPOS, 0, 0);
}

void WinProgress::SetProgress(ULONGLONG target) {
    using namespace LAULL_PROGRESS_WIN;
    SendMessage(item_p->progress_bar.hwnd, PBM_SETPOS, target, 0);
}

void WinProgress::Addprogress(ULONGLONG add) {
    SetProgress(GetProgress() + add);
}

void WinProgress::SetTotal(ULONGLONG total) {
    using namespace LAULL_PROGRESS_WIN;
    item_p->_total = total;
    item_p->_init_num = GetProgress();
    // DestroyWindow(progress_bar.hwnd);
    SendMessage(item_p->progress_bar.hwnd, PBM_SETRANGE, 0, MAKELPARAM(0, item_p->_total));
    SendMessage(item_p->progress_bar.hwnd, PBM_SETPOS, item_p->_init_num, 0);
}

int WinProgress::WaitUntilFinish(){
    while (!finish)
        Sleep(10);
    return RETURN;
}

WinProgress::~WinProgress() {
    this->Close();
    CloseHandle(item_p->hwnd);
    delete item_p;
}